home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-05 / chatr2.zip / CHATTER.C < prev    next >
C/C++ Source or Header  |  1992-09-21  |  38KB  |  1,086 lines

  1. /**************************************************************
  2.  
  3.   Chatter
  4.   Copyright (c) 1992 Kurt Duncan - All Rights Reserved
  5.  
  6.   An interactive communications program based on the NOVELLtm
  7.   IPX/SPX transport mechanism.
  8.  
  9.   Command line:
  10.     CHATTER [<ident>] [<options>]
  11.  
  12.   Options:
  13.     -r<rows on display>
  14.        sets number of rows of display (25, 43, 50)
  15.        defaults to 25
  16.  
  17.     -m
  18.        indicates a monochrome monitor
  19.  
  20.     -s<hex_socket_number>
  21.        indicates the IPX socket number to be used
  22.        defaults to 5000h
  23.  
  24.     -b<number_of_buffers>
  25.        indicates the number of ECB/IPX_header buffers
  26.          that will be allocated for receiving network traffic
  27.        defaults to 16
  28.  
  29.     -n<hex-network-number>
  30.        a network number to be added to the list of broadcast
  31.        networks
  32.  
  33. **************************************************************/
  34.  
  35. #include <stdio.h>
  36. #include <string.h>
  37. #include <dos.h>
  38. #include "ipxlib.h"
  39.  
  40.  
  41. #define PROCESSOR_NAME       "CHATTER V1.20"
  42.  
  43.  
  44. /*****************************************************************
  45.   The following codes are arbitrary values which we will use
  46.   to classify the various messages we are going to deal with.
  47.   The code is stored as the first data byte in each IPX message.
  48. *****************************************************************/
  49.  
  50. #define JOIN_CODE            0x00
  51. #define LEAVE_CODE           0x01
  52. #define PROBE_CODE           0x02
  53. #define RESPOND_CODE         0x03
  54. #define MESSAGE_CODE         0x0F
  55.  
  56.  
  57. /******************************************************************
  58.   Stuff for dealing with color attributes... and which
  59.   automatically choose monochrome attributes if Color_Display == 0
  60. ******************************************************************/
  61.  
  62. #define COLOR_TEST(cv, mv)   (unsigned char) (Color_Display ? cv : mv)
  63. #define WHITE_ON_BLACK       0x0F
  64. #define GREY_ON_BLACK        0x07
  65. #define BLACK_ON_WHITE       0x70
  66. #define WHITE_ON_RED         0x4F
  67. #define YELLOW_ON_BLACK      0x0E
  68. #define GREEN_ON_BLACK       0x0A
  69. #define CYAN_ON_BLACK        0x0B
  70. #define CYAN_ON_BLUE         0x1B
  71. #define YELLOW_ON_GREEN      0x2E
  72.  
  73. #define PROCESSOR_LINE_COLOR COLOR_TEST (WHITE_ON_RED,    BLACK_ON_WHITE)
  74. #define TEXT_COLOR           COLOR_TEST (GREEN_ON_BLACK,  GREY_ON_BLACK)
  75. #define JOIN_COLOR           COLOR_TEST (WHITE_ON_RED,    BLACK_ON_WHITE)
  76. #define IDENT_COLOR          COLOR_TEST (CYAN_ON_BLACK,   GREY_ON_BLACK)
  77. #define MESSAGE_COLOR        COLOR_TEST (YELLOW_ON_BLACK, WHITE_ON_BLACK)
  78. #define INPUT_COLOR          COLOR_TEST (CYAN_ON_BLUE,    BLACK_ON_WHITE)
  79. #define TRACE_COLOR          COLOR_TEST (YELLOW_ON_GREEN, BLACK_ON_WHITE)
  80.  
  81.  
  82. /****************************************
  83.   Macros which think they are functions
  84. ****************************************/
  85.  
  86. #define Screen_Write(p)     Screen_Write_Attr (p, Screen_Attribute)
  87. #define Screen_Append(p)    Screen_Append_Attr (p, Screen_Attribute)
  88.  
  89. #define MK_FP(seg, off) (void far *) ((((unsigned long) seg) << 16) | (off))
  90.  
  91.  
  92. /*********************************************************
  93.   Functions which are described further down in the code
  94. *********************************************************/
  95.  
  96. void Show_Node_Address   (unsigned char *Node, unsigned char *Msg);
  97. void Clear_Buffer        (void);
  98. void Screen_Clear        (void);
  99. void Screen_ClearLast    (void);
  100. void Screen_Scroll       (void);
  101. void Screen_Write_Attr   (char *Inst, char Attr);
  102. int  Initialization      (int argc, char *argv[]);
  103. void Termination         (void);
  104. void Setup_Send          (char Code, char *Inmsg);
  105. void Setup_Send_Specific (char Code, char *Inmsg, struct IPX_address *Addr);
  106. void Execute_Command     (char *CString);
  107. void Poll                (void);
  108. void Poll_Key            (void);
  109. void Poll_Net            (void);
  110.  
  111.  
  112. /***********************************************************
  113.   Various Data Structures are defined and/or declared here
  114. ***********************************************************/
  115.  
  116. struct Character_Cell {  /* A definition of a screen position in video  */
  117.     char Character;      /* memory.  Such memory is assumed to begin at */
  118.     char Attribute;      /* address B800:0000.                          */
  119.     };
  120.  
  121.  
  122. struct IPX_ECB     Send_ECB;    /* An ECB packet for sending data        */
  123. struct IPX_header  Send_IPXH;   /* An IPX header packet for sending data */
  124. unsigned int       Buffers;     /* The number of buffers we will support */
  125. struct IPX_ECB    *ECBPointer;  /* Pointer to first ECB buffer           */
  126. struct IPX_header *IPXHPointer; /* Pointer to first IPX header buffer    */
  127.  
  128. char         Buffer[80];        /* Input buffer - holds user input   */
  129. unsigned int Bufsub;            /* Current number of chars in buffer */
  130.  
  131. unsigned int Socket_Number;     /* IPX socket number we communicate on   */
  132. unsigned int Color_Display;     /* zero if monochrome, nonzero otherwise */
  133. char         Ident[9];          /* user's identifier, from command line  */
  134.  
  135. struct Character_Cell far *Screen; /* Pointer to video memory              */
  136. char         Screen_Attribute;     /* Default attribute for erasing screen */
  137. unsigned int Screen_Rows;          /* Number of rows on screen             */
  138. unsigned int Append_Suboff;        /* Last screen position written to      */
  139.  
  140. unsigned int Trace;             /* Trace flag; nonzero indicates trace on */
  141. unsigned int All_Done;          /* nonzero indicates user wants to quit   */
  142.  
  143. #define NetNumCountMax    256
  144. unsigned int NetNumCount;       /* Stuff to support multiple networks */
  145. unsigned long int NetworkNumber[NetNumCountMax];
  146.  
  147.  
  148. /******************************************************************
  149.   The following routine takes the Node portion of an IPXH_address
  150.   structure, and converts the six byte field to an ASCII string
  151.   that can be displayed.  An example of the output would look
  152.   similar to:
  153.  
  154.         00.00.1B.2C.19.37
  155. ******************************************************************/
  156.  
  157. void Show_Node_Address (unsigned char *Node, unsigned char *Msg) 
  158. {
  159.     int  inx, outx, lcv;
  160.     char ch, cl;
  161.  
  162.     inx = 0;
  163.     outx = 0;
  164.     for (lcv = 0; lcv < 6; lcv++) 
  165.     {
  166.         ch = (Node[inx] >> 4) | 0x30;
  167.         if (ch > '9') ch += 7;
  168.         cl = (Node[inx++] & 0x0F) | 0x30;
  169.         if (cl > '9') cl += 7;
  170.         Msg[outx++] = ch;
  171.         Msg[outx++] = cl;
  172.         if (lcv < 5) Msg[outx++] = '.';
  173.     }
  174.     Msg[outx] = 0x00;
  175.     return;
  176. }
  177.  
  178.  
  179. /****************************************************************
  180.   The following routine sets all of the bytes of the internally
  181.   maintained input buffer to binary zero.
  182. ****************************************************************/
  183.  
  184. void Clear_Buffer (void) 
  185. {
  186.     int sub;
  187.  
  188.     Bufsub = 0;
  189.     for (sub = 0; sub < 80; sub++)
  190.         Buffer[sub] = 0;
  191.     return;
  192. }
  193.  
  194.  
  195. /*****************************************************************
  196.   Using direct memory access, we set the entire screen to blanks
  197.   (hex 0x20), with an attribute value as specified by 
  198.   Screen_Attribute.
  199. *****************************************************************/
  200.  
  201. void Screen_Clear (void) 
  202. {
  203.     int sub;
  204.     
  205.     for (sub = 0; sub < (Screen_Rows * 80); sub++) 
  206.     {
  207.         Screen[sub].Character = 0x20;
  208.         Screen[sub].Attribute = Screen_Attribute;
  209.     }
  210.     return;
  211. }
  212.  
  213.  
  214. /************************************************************
  215.   Sets the last line of the display to all blanks, with the
  216.   attribute specified by INPUT_COLOR.
  217. ************************************************************/
  218.  
  219. void Screen_ClearLast (void) 
  220. {
  221.     unsigned int sub, cnt;
  222.  
  223.     sub = (Screen_Rows - 1) * 80;
  224.     for (cnt = 0; cnt < 80; cnt++) 
  225.     {
  226.         Screen[sub].Character = 0x20;
  227.         Screen[sub++].Attribute = INPUT_COLOR;
  228.     }
  229.     return;
  230. }
  231.  
  232.  
  233. /************************************************************
  234.   Scrolls the display area of the screen (basically, all of
  235.   the screen except for the last row) up by one line.  The
  236.   last line of the display area is set to blanks, with the
  237.   attribute specified by Screen_Attribute.
  238. ************************************************************/
  239.  
  240. void Screen_Scroll (void) 
  241. {
  242.     unsigned int sub, suboff;
  243.  
  244.     suboff = 80;
  245.     for (sub = 0; sub < (Screen_Rows - 2) * 80; sub++) 
  246.     {
  247.         Screen[sub].Character = Screen[suboff].Character;
  248.         Screen[sub].Attribute = Screen[suboff].Attribute;
  249.         suboff++;
  250.     }
  251.     suboff = (Screen_Rows - 2) * 80;
  252.     for (sub = 0; sub < 80; sub++) 
  253.     {
  254.         Screen[suboff].Character = 0x20;
  255.         Screen[suboff++].Attribute = Screen_Attribute;
  256.     }
  257.     return;
  258. }
  259.  
  260.  
  261. /****************************************************************
  262.   Scrolls the display area, and writes the given string to the
  263.   last line of the display area, using the specified attribute.
  264.   We keep track of the last screen position written to, in case
  265.   the calling routine decides to call Screen_Append_Attr.
  266.  
  267.   The Screen_Write macro calls this function, with the
  268.   Screen_Attribute value as the second parameter.
  269. ****************************************************************/
  270.  
  271. void Screen_Write_Attr (char *Inst, char Attr) 
  272. {
  273.     unsigned int sub, limit, suboff;
  274.  
  275.     Screen_Scroll ();
  276.     limit = strlen (Inst) + 1;
  277.     if (limit > 80) limit = 80;
  278.     suboff = (Screen_Rows - 2) * 80;
  279.     for (sub = 0; sub < limit; sub++) {
  280.         Screen[suboff].Character = Inst[sub];
  281.         Screen[suboff++].Attribute = Attr;
  282.         }
  283.     Append_Suboff = suboff;
  284.     return;
  285. }
  286.  
  287.  
  288. /***************************************************************
  289.   Writes the specified output to the display area, starting at
  290.   the position which follows the last position written to by
  291.   either Screen_Write_Attr or Screen_Append_Attr, using the
  292.   attribute specified.  We keep track of the last screen
  293.   position written to, in case the calling routine decides to
  294.   call us again.
  295.  
  296.   The Screen_Append macro calls this function, with the
  297.   Screen_Attribute value as the second parameter.
  298. ***************************************************************/
  299.  
  300. void Screen_Append_Attr (char *Inst, char Attr) {
  301.     unsigned int sub, limit, suboff;
  302.  
  303.     limit = strlen (Inst);
  304.     suboff = Append_Suboff;
  305.     for (sub = 0; sub < limit; sub++) {
  306.         Screen[suboff].Character = Inst[sub];
  307.         Screen[suboff++].Attribute = Attr;
  308.         }
  309.     Append_Suboff = suboff;
  310.     return;
  311.     }
  312.  
  313.  
  314. /*****************************************************************
  315.   We do a lot of stuff here.  See embedded comments for details.
  316.   This code happends once, when the program first executes.  The
  317.   code has been placed here, so that the main function can stay
  318.   relatively clean.  Basically, we do all setup, and return a
  319.   zero value (false) if something went wrong.  Otherwise, we
  320.   return a nonzero value (true) to indicate to main that all is
  321.   okay.
  322. *****************************************************************/
  323.  
  324. int Initialization (int argc, char *argv[]) 
  325. {
  326.     union REGS   regs;
  327.     unsigned int sub, scsub;
  328.     char         tempst[80];
  329.     void far *fp;
  330.  
  331.   /* Check whether IPX is loaded */
  332.  
  333.     if (!(IPX_Is_Loaded ())) 
  334.     {
  335.         printf ("IPX is not loaded!\n");
  336.         return (0);
  337.     }
  338.  
  339.   /* Set default values                            */
  340.   /* Default socket is 5000 hex,                   */
  341.   /* Default display mode is color,                */
  342.   /* trace is cleared,                             */
  343.   /* buffers is set to 16,                         */
  344.   /* base screen address is set to B800:0000,      */
  345.   /* screen rows is set to 25,                     */
  346.   /* screen attribute is set to dim white on black */
  347.   /* Number of network number entries is set to 0  */
  348.  
  349.     Socket_Number = 0x5000;
  350.     strcpy (Ident, "<anon>  ");
  351.     Color_Display = 1;
  352.     Trace = 0;
  353.     Buffers = 16;
  354.     Screen = MK_FP (0xB800, 0x0000);
  355.     Screen_Rows = 25;
  356.     Screen_Attribute = 0x0007;
  357.     NetNumCount = 0;
  358.  
  359.   /* Read command line parameters                                  */
  360.   /* The first parameter without a leading hyphen is considered to */
  361.   /* be an ident code.  The next such parameter causes an error.   */
  362.   /* All parameters which start with a hyphen are considered       */
  363.   /* switches, and are compared against the valid switches.        */
  364.  
  365.     for (sub = 1; sub < argc; sub++) 
  366.     {
  367.         if (strlen (argv[sub]) > 20) 
  368.         {
  369.             printf ("Parameter %u is too long\n", sub);
  370.             return (0);
  371.         }
  372.         strcpy (tempst, argv[sub]);
  373.         if (tempst[0] != '-') 
  374.         {
  375.             if (strlen (tempst) > 8) 
  376.             {
  377.                 printf ("Ident code \"%s\" at parameter %u is too long - \n"
  378.                         "  limit is eight characters\n", tempst, sub);
  379.                 return (0);
  380.             }
  381.             if (strcmp (Ident, "<anon>  ") != 0) 
  382.             {
  383.                 printf ("Unrecognized text \"%s\" at parameter %u\n",
  384.                         tempst, sub);
  385.                 return (0);
  386.             }
  387.             else 
  388.             {
  389.                 strcpy (Ident, tempst);
  390.                 while (strlen (Ident) < 8)
  391.                     strcat (Ident, " ");
  392.             }
  393.         }
  394.         else 
  395.         {
  396.             char t2[19];
  397.             int  ti;
  398.             unsigned long int nn;
  399.  
  400.             strcpy (t2, &tempst[2]);
  401.             switch (tempst[1]) 
  402.             {
  403.                 case 'r':
  404.                 case 'R':
  405.                     ti = sscanf (t2, "%u", &Screen_Rows);
  406.                     if ((ti == 0) || (Screen_Rows > 50)) 
  407.                     {
  408.                         printf ("Invalid number of rows given - "
  409.                                 "Option \"%s\" at parameter %u\n",
  410.                                 tempst, sub);
  411.                         return (0);
  412.                     }
  413.                     break;
  414.                 case 'm':
  415.                 case 'M':
  416.                     if (t2[0] != 0x00)
  417.                         printf ("Extraneous text \"%s\" on -m option "
  418.                                 "at parameter %u ignored\n",
  419.                                 t2, sub);
  420.                     Color_Display = 0;
  421.                     break;
  422.                 case 's':
  423.                 case 'S':
  424.                     ti = sscanf (t2, "%X", &Socket_Number);
  425.                     if (ti == 0) 
  426.                     {
  427.                         printf ("Invalid Socket Number given - "
  428.                                 "Option \"%s\" at parameter %u\n",
  429.                                 tempst, sub);
  430.                         return (0);
  431.                     }
  432.                     break;
  433.                 case 'b':
  434.                 case 'B':
  435.                     ti = sscanf (t2, "%u", &Buffers);
  436.                     if (ti == 0) 
  437.                     {
  438.                         printf ("Invalid Buffer count given - "
  439.                                 "Option \"%s\" at parameter %u\n",
  440.                                 tempst, sub);
  441.                         return (0);
  442.                     }
  443.                     break;
  444.                 case 'n':
  445.                 case 'N':
  446.                     ti = sscanf (t2, "%lX", &nn);
  447.                     if (ti == 0) 
  448.                     {
  449.                         printf ("Invalid Network Number given - "
  450.                                 "Option \"%s\" at parameter %u\n", 
  451.                                 tempst, sub);
  452.                         break;
  453.                     }
  454.                     if (NetNumCount == NetNumCountMax)
  455.                     {
  456.                         printf ("Internal Network Number table is full - "
  457.                                 "Ignoring Option \"%s\" at parameter %u\n",
  458.                                 tempst, sub);
  459.                         break;
  460.                     }
  461.                     NetworkNumber[NetNumCount] = nn;
  462.                     NetNumCount++;
  463.                     break;
  464.                 default:
  465.                     printf ("Unrecognized option \"%s\" at parameter %u\n",
  466.                             tempst, sub);
  467.                     return (0);
  468.             }
  469.         }
  470.     }
  471.  
  472.   /* Allocate buffer space for listen ECB's and IPX headers,  */
  473.   /* then initialize the relevant ECB fields.  Finally, set   */
  474.   /* up all the buffers for listening.                        */
  475.   /* We use multiple buffers because it is certain that we    */
  476.   /* will, sooner or later, encounter a condition where we    */
  477.   /* cannot fully process an input message before another one */
  478.   /* is received.  Thus, we chain several buffers to our      */
  479.   /* socket, and let IPX use them up one by one.  As we       */
  480.   /* process them, we set them back up for listening.  If we  */
  481.   /* are lucky, we will never get more traffic than we can    */
  482.   /* handle.                                                  */
  483.   
  484.     (void *) ECBPointer = 
  485.         (void *) malloc (Buffers * sizeof (struct IPX_ECB));
  486.  
  487.     if (ECBPointer == NULL) 
  488.     {
  489.         printf ("Unable to allocate memory for ECB buffers\n");
  490.         return (0);
  491.     }
  492.     
  493.     (void *) IPXHPointer = 
  494.         (void *) malloc (Buffers * sizeof (struct IPX_header));
  495.  
  496.     if (IPXHPointer == NULL) 
  497.     {
  498.         free (ECBPointer);
  499.         printf ("Unable to allocate memory for IPX header buffers\n");
  500.         return (0);
  501.     }
  502.     
  503.     for (sub = 0; sub < Buffers; sub++) 
  504.     {
  505.         ECBPointer[sub].In_Use = 01;
  506.         ECBPointer[sub].ESR_Address.segment = 0;
  507.         ECBPointer[sub].ESR_Address.offset = 0;
  508.         ECBPointer[sub].Socket_Number = IPX_Flipword (Socket_Number);
  509.         ECBPointer[sub].Fragment_Count = 1;
  510.         fp = &IPXHPointer[sub];
  511.         ECBPointer[sub].Fragment_Desc[0].Address.segment = FP_SEG (fp);
  512.         ECBPointer[sub].Fragment_Desc[0].Address.offset = FP_OFF (fp);
  513.         ECBPointer[sub].Fragment_Desc[0].Size = 576;
  514.     }
  515.  
  516.     IPX_Open_Socket (Socket_Number);
  517.     for (sub = 0; sub < Buffers; sub++)
  518.         IPX_Listen_For_Packet (&ECBPointer[sub]);
  519.  
  520.   /* Hide the DOS cursor */
  521.     regs.h.ah = 0x03;
  522.     int86 (0x10, ®s, ®s);
  523.     regs.h.ch |= 0x20;
  524.     regs.h.ah = 0x01;
  525.     int86 (0x10, ®s, ®s);
  526.  
  527.   /* Clear the screen, and set the buffer cursor (a simulated cursor) */
  528.     Screen_Clear ();
  529.     Screen_ClearLast ();
  530.     Clear_Buffer ();
  531.     scsub = (Screen_Rows - 1) * 80;
  532.     Screen[scsub].Character = '_';
  533.     Screen[scsub].Attribute = INPUT_COLOR;
  534.  
  535.   /* Display initial messages */
  536.     Screen_Write_Attr (PROCESSOR_NAME, PROCESSOR_LINE_COLOR);
  537.     sprintf (tempst, "Ident:   %s", Ident);
  538.     Screen_Write_Attr (tempst, TEXT_COLOR);
  539.     sprintf (tempst, "Socket:  %xh", Socket_Number);
  540.     Screen_Write_Attr (tempst, TEXT_COLOR);
  541.     sprintf (tempst, "Buffers: %u", Buffers);
  542.     Screen_Write_Attr (tempst, TEXT_COLOR);
  543.     Screen_Write_Attr ("Enter \\HELP for a list of commands",
  544.                        TEXT_COLOR);
  545.     
  546.   /* Send broadcast messages, type JOIN_CODE and PROBE_CODE */
  547.  
  548.     Setup_Send (JOIN_CODE, "");
  549.     Setup_Send (PROBE_CODE, "");
  550.     return (1);
  551. }
  552.  
  553.  
  554. /*************************************************************
  555.   Termination is fairly simple:
  556.  
  557.     Cancel all ECB packets.  The IPXLIB routines will ignore
  558.       such requests for all packets that might not currently
  559.       be on the listen queue.
  560.     Free the memory previously occupied by the ECB and IPX
  561.       header buffers.
  562.     Send one final message, LEAVE_CODE, to indicate to the
  563.       other users on the network that you are going away.
  564.     Close the IPX socket.
  565.     Clear the screen.
  566.     Restore the DOS cursor.
  567. *************************************************************/
  568.  
  569. void Termination (void) 
  570. {
  571.     union REGS regs;
  572.     int sub;
  573.  
  574.     for (sub = 0; sub < Buffers; sub++)
  575.         IPX_Cancel_Event (&ECBPointer[sub]);
  576.     free (IPXHPointer);
  577.     free (ECBPointer);
  578.  
  579.     Setup_Send (LEAVE_CODE, "");
  580.     IPX_Close_Socket (Socket_Number);
  581.     Screen_Clear ();
  582.  
  583.     regs.h.ah = 0x03;
  584.     int86 (0x10, ®s, ®s);
  585.     regs.h.ch &= 0xDF;
  586.     regs.h.ah = 0x01;
  587.     int86 (0x10, ®s, ®s);
  588.  
  589.     return;
  590. }
  591.  
  592.  
  593. /********************************************************************
  594.   The following routine sets up the ECB and IPX header packets for
  595.   sending the specified message.  The caller provides a message
  596.   code (JOIN_CODE, LEAVE_CODE, etc), and a message.  We send the 
  597.   message as a broadcast to each network in the network number table.
  598.   If the table is empty, we send to network 0 (the local network).
  599. ********************************************************************/
  600.  
  601. void Setup_Send (char Code, char *Inmsg) 
  602. {
  603.     void far *fp;
  604.     unsigned int sub;
  605.     char tmsg[80], cmsg[20], nmsg[30];
  606.     struct IPX_address addr;
  607.  
  608.     if (strlen (Inmsg) > 500) return;
  609.  
  610.     memcpy (addr.Node.ch, "\xFF\xFF\xFF\xFF\xFF\xFF", 6);
  611.     addr.Socket = IPX_Flipword (Socket_Number);
  612.  
  613.     if (NetNumCount == 0)
  614.     {
  615.         addr.Network = 0;
  616.         Setup_Send_Specific (Code, Inmsg, &addr); 
  617.     }
  618.     else
  619.         for (sub = 0; sub < NetNumCount; sub++)
  620.         {
  621.             addr.Network = NetworkNumber[sub];
  622.             IPX_Fliplong (&addr.Network);
  623.             Setup_Send_Specific (Code, Inmsg, &addr);
  624.         }
  625.  
  626.     return;
  627. }
  628.  
  629.  
  630. /********************************************************************
  631.   The following routine sets up the ECB and IPX header packets for
  632.   sending the specified message.  The caller provides a message
  633.   code (JOIN_CODE, LEAVE_CODE, etc), a message, and an IPX address
  634.   structure which indicates the node to which the message is to be
  635.   sent.  The first thing we do is set up the IPX header, which is
  636.   followed by the actual data.  The data sent is formatted as such:
  637.     Byte +00: message code             1 byte
  638.     Byte +01: Ident, from command line 8 bytes
  639.     Byte +09: message                  zero to 500 bytes
  640.   The next thing we do is we call the IPX_Get_Local_Target function
  641.   to determine what the immediate address in the ECB should be (in
  642.   case we need to cross through a router), and then we set up the
  643.   ECB packet.
  644.   If Trace is set, we send a message to the local display.
  645.   Finally, we send the message itself, and wait until we are sure
  646.   the message actually got sent (not necessarily received, though).
  647. ********************************************************************/
  648.  
  649. void Setup_Send_Specific (char Code, 
  650.                           char *Inmsg,
  651.                           struct IPX_address *Addr) 
  652. {
  653.     void far *fp;
  654.     char tmsg[80], cmsg[20], nmsg[30];
  655.  
  656.     if (strlen (Inmsg) > 500) return;
  657.  
  658.     Send_IPXH.Packet_Type = 0;
  659.     Send_IPXH.Destination.Network = Addr->Network;
  660.     memcpy (Send_IPXH.Destination.Node.ch, Addr->Node.ch, 6);
  661.     Send_IPXH.Destination.Socket = Addr->Socket;
  662.     Send_IPXH.Data[0] = Code;
  663.     memcpy (&Send_IPXH.Data[1], Ident, 8);
  664.     strcpy (&Send_IPXH.Data[9], Inmsg);
  665.  
  666.     IPX_Get_Local_Target (&Send_IPXH.Destination, 
  667.                           &Send_ECB.Immediate_Address);
  668.     Send_ECB.ESR_Address.segment = 0;
  669.     Send_ECB.ESR_Address.offset = 0;
  670.     Send_ECB.Socket_Number = IPX_Flipword (Socket_Number);
  671.     Send_ECB.Fragment_Count = 1;
  672.     fp = &Send_IPXH;
  673.     Send_ECB.Fragment_Desc[0].Address.segment = FP_SEG (fp);
  674.     Send_ECB.Fragment_Desc[0].Address.offset = FP_OFF (fp);
  675.     Send_ECB.Fragment_Desc[0].Size = 30 + 8 + strlen (Inmsg) + 1;
  676.  
  677.     if (Trace) 
  678.     {
  679.         switch (Send_IPXH.Data[0]) 
  680.         {
  681.             case JOIN_CODE:
  682.                 strcpy (cmsg, "JOIN   ");
  683.                 break;
  684.             case LEAVE_CODE:
  685.                 strcpy (cmsg, "LEAVE  ");
  686.                 break;
  687.             case PROBE_CODE:
  688.                 strcpy (cmsg, "PROBE  ");
  689.                 break;
  690.             case RESPOND_CODE:
  691.                 strcpy (cmsg, "RESPOND");
  692.                 break;
  693.             case MESSAGE_CODE:
  694.                 strcpy (cmsg, "MESSAGE");
  695.                 break;
  696.             default:
  697.                 sprintf (cmsg, "UNKNOWN");
  698.                 break;
  699.         }
  700.         Show_Node_Address (Send_IPXH.Destination.Node.ch, nmsg);
  701.         sprintf (tmsg, " - Sending %s packet  to  Network %lu Node ",
  702.                  cmsg, Send_IPXH.Destination.Network);
  703.         strcat (tmsg, nmsg);
  704.         Screen_Write_Attr (tmsg, TRACE_COLOR);
  705.     }
  706.  
  707.     IPX_Send_Packet (&Send_ECB);
  708.     while (Send_ECB.In_Use == 0xFF)
  709.         IPX_Relinquish_Control ();
  710.  
  711.     return;
  712. }
  713.  
  714.  
  715. /**********************************************************************
  716.   This function executes the command which is passed as the argument.
  717.   Valid commands include \EXIT, \HELP, \TRACE, and \WHO.  These
  718.   commands will be keyed by the user, or invoked as a result of a
  719.   special keystroke such as ESC, F1, etc.
  720.  
  721.   The \EXIT command sets All_Done to non-zero (true), which will
  722.     eventually cause CHATTER to terminate.
  723.   The \HELP command causes a list of valid commands to be sent to the
  724.     display area.
  725.   The \TRACE command toggles the Trace identifier between zero and
  726.     nonzero states, with zero indicating Trace-is-off.
  727.   The \PROBE command broadcasts a PROBE message.  The message is
  728.     transparent to the user, but causes all receiving stations to
  729.     transmit a RESPOND message to the originating station.
  730. **********************************************************************/
  731.  
  732. void Execute_Command (char *CString)
  733. {
  734.     char         temp[32];
  735.     unsigned int sub;
  736.  
  737.     memcpy (temp, CString, 9);
  738.     temp[9] = 0x00;
  739.     sub = 0;
  740.  
  741.     while (sub < strlen (CString)) 
  742.     {
  743.         if (temp[sub] == 32) 
  744.         {
  745.             temp[sub] = 0x00;
  746.             sub = 9;
  747.         }
  748.         if (temp[sub] >= 'a')
  749.             temp[sub] -= 32;
  750.         sub++;
  751.     }
  752.  
  753.     if (strcmp (temp, "\\EXIT") == 0) 
  754.     {
  755.         All_Done = 1;
  756.         return;
  757.     }
  758.  
  759.     if (strcmp (temp, "\\HELP") == 0) 
  760.     {
  761.         Screen_Write_Attr ("List of commands:", TEXT_COLOR);
  762.         Screen_Write_Attr ("  \\EXIT  or ESC: Terminate CHATTER",
  763.                            TEXT_COLOR);
  764.         Screen_Write_Attr ("  \\HELP  or F1:  This Display", TEXT_COLOR);
  765.         Screen_Write_Attr ("  \\TRACE or F2:  Toggles Trace Mode", 
  766.                            TEXT_COLOR);
  767.         Screen_Write_Attr ("  \\WHO   or F3:  List of Conferencees",
  768.                            TEXT_COLOR);
  769.         return;
  770.     }
  771.  
  772.     if (strcmp (temp, "\\TRACE") == 0) 
  773.     {
  774.         if (Trace) 
  775.         {
  776.             Trace = 0;
  777.             Screen_Write_Attr ("TRACE is now off", TEXT_COLOR);
  778.         }
  779.         else 
  780.         {
  781.             Trace = 1;
  782.             Screen_Write_Attr ("TRACE is now on", TEXT_COLOR);
  783.         }
  784.         return;
  785.     }
  786.  
  787.     if (strcmp (temp, "\\WHO") == 0) 
  788.     {
  789.         Setup_Send (PROBE_CODE, "");
  790.         return;
  791.     }
  792.  
  793.     if (strcmp (temp, "\\NETS") == 0)
  794.     {
  795.         Screen_Write_Attr ("List of Active Networks:", TEXT_COLOR);
  796.         for (sub = 0; sub < NetNumCount; sub++)
  797.         {
  798.             sprintf (temp, "  %.8lXh", NetworkNumber[sub]);
  799.             Screen_Write_Attr (temp, TEXT_COLOR);
  800.         }
  801.         return;
  802.     }
  803.  
  804.     return;
  805.     }
  806.  
  807.  
  808. /******************************************************************
  809.   This function checks all of the listen buffers to see if any of
  810.   them have a message.  If so, the message is processed, and the
  811.   buffer is returned to the listen chain via the IPXLIB call
  812.   IPX_Listen_For_Packet.  As soon as we find a message that has
  813.   been received, assuming trace is set, we compose and display
  814.   a message indicating the type of message, and the source.
  815.   Most message result in some kind of message being sent to the
  816.   display area, except for the PROBE message.  If we receive a
  817.   PROBE message, we compose a RESPOND message and send it back to
  818.   whichever station sent the original PROBE message.
  819.  
  820.   Also note that, for all messages, we take the source network
  821.   number and add it to our network number table if it isn't
  822.   already there.
  823. ******************************************************************/
  824.  
  825. void Poll_Net (void) 
  826. {
  827.     int  dsize;
  828.     char msg[9], tmsg[80], cmsg[20], nmsg[30];
  829.     int  sub, sub2;
  830.     unsigned long normnet;
  831.  
  832.     for (sub = 0; sub < Buffers; sub++) 
  833.     {
  834.         if ((ECBPointer[sub].In_Use == 0) &&
  835.           (ECBPointer[sub].Completion_Code == 0)) 
  836.           {
  837.             if (Trace) 
  838.             {
  839.                 switch (IPXHPointer[sub].Data[0]) 
  840.                 {
  841.                     case JOIN_CODE:
  842.                         strcpy (cmsg, "JOIN   ");
  843.                         break;
  844.                     case LEAVE_CODE:
  845.                         strcpy (cmsg, "LEAVE  ");
  846.                         break;
  847.                     case PROBE_CODE:
  848.                         strcpy (cmsg, "PROBE  ");
  849.                         break;
  850.                     case RESPOND_CODE:
  851.                         strcpy (cmsg, "RESPOND");
  852.                         break;
  853.                     case MESSAGE_CODE:
  854.                         strcpy (cmsg, "MESSAGE");
  855.                         break;
  856.                     default:
  857.                         sprintf (cmsg, "UNKNOWN");
  858.                         break;
  859.                 }
  860.                 Show_Node_Address (IPXHPointer[sub].Source.Node.ch, nmsg);
  861.                 sprintf (tmsg, " - Reading %s packet from Network %u Node ",
  862.                          cmsg, IPXHPointer[sub].Source.Network);
  863.                 strcat (tmsg, nmsg);
  864.                 Screen_Write_Attr (tmsg, TRACE_COLOR);
  865.             }
  866.             dsize = IPX_Flipword (IPXHPointer[sub].Length) - 30;
  867.             IPXHPointer[sub].Data[dsize] = 0x00;
  868.             switch (IPXHPointer[sub].Data[0]) 
  869.             {
  870.                 case JOIN_CODE:
  871.                     memcpy (msg, &IPXHPointer[sub].Data[1], 8);
  872.                     msg[8] = 0x00;
  873.                     Screen_Write_Attr (msg, IDENT_COLOR);
  874.                     Screen_Append (" ");
  875.                     Screen_Append_Attr ("<< Joining Conference >>", 
  876.                                         JOIN_COLOR);
  877.                     break;
  878.                 case LEAVE_CODE:
  879.                     memcpy (msg, &IPXHPointer[sub].Data[1], 8);
  880.                     msg[8] = 0x00;
  881.                     Screen_Write_Attr (msg, IDENT_COLOR);
  882.                     Screen_Append (" ");
  883.                     Screen_Append_Attr ("<< Leaving Conference >>", 
  884.                                         JOIN_COLOR);
  885.                     break;
  886.                 case PROBE_CODE:
  887.                     Setup_Send_Specific (RESPOND_CODE, "", 
  888.                                          &IPXHPointer[sub].Source);
  889.                     break;
  890.                 case RESPOND_CODE:
  891.                     memcpy (msg, &IPXHPointer[sub].Data[1], 8);
  892.                     msg[8] = 0x00;
  893.                     Screen_Write_Attr (msg, IDENT_COLOR);
  894.                     Screen_Append (" ");
  895.                     Screen_Append_Attr ("<< Responding to probe >>", 
  896.                                         JOIN_COLOR);
  897.                     break;
  898.                 case MESSAGE_CODE:
  899.                     memcpy (msg, &IPXHPointer[sub].Data[1], 8);
  900.                     msg[8] = 0x00;
  901.                     Screen_Write_Attr (msg, IDENT_COLOR);
  902.                     Screen_Append (" ");
  903.                     if (strlen (&IPXHPointer[sub].Data[9]) > 70)
  904.                         IPXHPointer[sub].Data[79] = 0x00;
  905.                     Screen_Append_Attr (&IPXHPointer[sub].Data[9], 
  906.                                         MESSAGE_COLOR);
  907.                     break;
  908.             }
  909.  
  910.             normnet = IPXHPointer[sub].Source.Network;
  911.             IPX_Fliplong (&normnet);
  912.             for (sub2 = 0; sub2 < NetNumCount; sub2++)
  913.                 if (normnet == NetworkNumber[sub2])
  914.                     break;
  915.             if (sub2 == NetNumCount)
  916.             {
  917.                 if (NetNumCount == NetNumCountMax)
  918.                 {
  919.                     Screen_Write_Attr ("Network Number Table Overflow",
  920.                                        TEXT_COLOR);
  921.                 }
  922.                 else
  923.                 {
  924.                     NetworkNumber[NetNumCount] = normnet;
  925.                     NetNumCount++;
  926.                 }
  927.             }
  928.  
  929.             IPX_Listen_For_Packet (&ECBPointer[sub]);
  930.         }
  931.     }
  932.     return;
  933. }
  934.  
  935.  
  936. /**********************************************************************
  937.   This function uses DOS interrupt 21h to read keystrokes.  If there
  938.   are no keystrokes waiting for us, we just return.  Otherwise, we
  939.   look at what we got, and take action accordingly.
  940.  
  941.   If we get a backspace (hex 08), we erase the most recent input
  942.     character from the input area, and back up the Bufsub counter.
  943.   If we get a carraige return (hex 0D), we set up the entire input
  944.     buffer as a network message and broadcast it, unless the first
  945.     input character is a backslash, in which case we try to interpret
  946.     the input buffer as a valid command (via Execute_Command).
  947.   If we get a two-byte sequence, we evaluate the second byte.  If we
  948.     have an F1, F2, F3, or F4, we call Execute_Command.  Otherwise, we
  949.     ignore the keystroke.
  950.   If we have an ESC key (hex 1B) we call Execute_Command.
  951.   If we have a control character, we ignore it.
  952.   Anything that gets through the previous checks is considered valid,
  953.     and is copied to the input buffer.
  954. **********************************************************************/
  955.  
  956. void Poll_Key (void) 
  957. {
  958.     union REGS regs;
  959.     int scsub;
  960.  
  961.     regs.h.ah = 0x0B;                  /* look for buffered characters */
  962.     int86 (0x21, ®s, ®s);
  963.     if (regs.h.al == 0x00) return;     /* return if there aren't any   */
  964.  
  965.     regs.h.ah = 0x08;                  /* otherwise, go get the next one */
  966.     int86 (0x21, ®s, ®s);
  967.  
  968.     if (regs.h.al == 0x08) 
  969.     {
  970.         if (Bufsub == 0) return;
  971.         scsub = (Screen_Rows - 1) * 80 + Bufsub;
  972.         Screen[scsub].Character = 0x20;
  973.         Screen[scsub].Attribute = INPUT_COLOR;
  974.         Screen[scsub - 1].Character = '_';
  975.         Screen[scsub - 1].Attribute = INPUT_COLOR;
  976.         Bufsub--;
  977.         return;
  978.     }
  979.  
  980.     if (regs.h.al == 0x0D) 
  981.     {
  982.         if (Buffer[0] == '\\') 
  983.         {
  984.             Execute_Command (Buffer);
  985.             Screen_ClearLast ();
  986.             Clear_Buffer ();
  987.             scsub = (Screen_Rows - 1) * 80;
  988.             Screen[scsub].Character = '_';
  989.             Screen[scsub].Attribute = INPUT_COLOR;
  990.             return;
  991.         }
  992.         Buffer [Bufsub] = 0x00;
  993.         Setup_Send (MESSAGE_CODE, Buffer);
  994.         Screen_ClearLast ();
  995.         Clear_Buffer ();
  996.         scsub = (Screen_Rows - 1) * 80;
  997.         Screen[scsub].Character = '_';
  998.         Screen[scsub].Attribute = INPUT_COLOR;
  999.         return;
  1000.     }
  1001.  
  1002.     if (regs.h.al == 0x00) 
  1003.     {
  1004.         regs.h.ah = 0x08;
  1005.         int86 (0x21, ®s, ®s);
  1006.         switch (regs.h.al) 
  1007.         {
  1008.             case 59:
  1009.                 Execute_Command ("\\HELP");
  1010.                 return;
  1011.             case 60:
  1012.                 Execute_Command ("\\TRACE");
  1013.                 return;
  1014.             case 61:
  1015.                 Execute_Command ("\\WHO");
  1016.                 return;
  1017.             case 62:
  1018.                 Execute_Command ("\\NETS");
  1019.                 return;
  1020.         }
  1021.         return;
  1022.     }
  1023.  
  1024.     if (regs.h.al == 0x1B) 
  1025.     {
  1026.         Execute_Command ("\\EXIT");
  1027.         return;
  1028.     }
  1029.  
  1030.     if (regs.h.al < 0x20) return;
  1031.  
  1032.     Buffer [Bufsub] = regs.h.al;
  1033.     scsub = (Screen_Rows - 1) * 80 + Bufsub;
  1034.     Screen[scsub].Character = regs.h.al;
  1035.     Screen[scsub].Attribute = INPUT_COLOR;
  1036.     if (Bufsub < 78) 
  1037.     {
  1038.         Bufsub++;
  1039.     }
  1040.  
  1041.     Screen[scsub + 1].Character = '_';
  1042.     Screen[scsub + 1].Attribute = INPUT_COLOR;
  1043.     return;
  1044. }
  1045.  
  1046.  
  1047. /**********************************************************************
  1048.   This routine is a simple way to control the invocation of the other
  1049.   various poll routines.  It really does nothing, in and of itself,
  1050.   other than calling the Relinquish IPX function, so that IPX can get
  1051.   some CPU time.
  1052. **********************************************************************/
  1053.  
  1054. void poll (void) 
  1055. {
  1056.     union REGS regs;
  1057.  
  1058.     Poll_Key ();
  1059.     Poll_Net ();
  1060.     IPX_Relinquish_Control ();
  1061.     return;
  1062. }
  1063.  
  1064.  
  1065. /**********************************************************************
  1066.   This is where execution starts.  We print the processor name, then
  1067.   call the Initialization function.  If the function returns zero
  1068.   (false) indicating an error condition, we just quit.  Otherwise,
  1069.   we clear the All_Done flag, then call the poll routine until the
  1070.   All_Done flag is non-zero.  At that point, we call the Termination
  1071.   function, then we return to DOS.  (Actually, to the C startup code,
  1072.   but that is a different story).
  1073. **********************************************************************/
  1074.  
  1075. int main (int argc, char *argv[]) 
  1076. {
  1077.     printf ("%s\n", PROCESSOR_NAME);
  1078.     if (Initialization (argc, argv) == 0x00) return (0x21);
  1079.     All_Done = 0;
  1080.     while (!(All_Done))
  1081.         poll ();
  1082.     Termination ();
  1083.     return (0x00);
  1084. }
  1085.  
  1086.